# 16. 文件操作

# 初始文件操作

使用Python来读写文件是非常简单的操作,可以使用open()函数来打开一个文件

获取文件句柄,然后通过文件句柄就可以进行各种各样的操作

根据打开方式的不同能够执行的操作也有有相应的差异

打开文件的方式:

  1. r (读取文件)
    1. w (写入,如果文件存在则清空所有内容,如果文件不存在则创建生成)
    2. a (追加写入文件内容,默认从结尾写入,不会清空文件原来数据,文件不存在则创建生成)
    3. r+ (读写操作,需要先读在写)
    4. w+ (写读操作,需要先写后读,这样就会读不出来,需要使用seek()函数,就可以读出来)
    5. a+(不常用)
    6. rb (读取文件以bytes类型显示)
    7. wb (bytes类型数据写入,如果文件存在则清空所有内容,如果文件不存在则创建生成)
    8. ab (追加写入bytes类型数据到文件内容,默认从结尾写入,不会清空文件原来数据,文件不存在则创建生成)
    9. r+b (不常用)
    10. w+b(不常用)
    11. a+b(不常用)

# 开启文件句柄 - open()

文件句柄是用来操作文件的

格式:open("文件名",选项)

选项:

1. mode=""             打开文件的方式权限
2. encoding=""        使用什么编码打开文件(解码)

# 关闭文件句柄 - close()

关闭文件句柄,建议操作文件完毕后,需要关闭文件句柄

格式:变量.close()

so.close()

# 刷新文件 - flush()

如果在追加或定入文件后,如果没有系统有一定的延时,数据还没有完整追加或写入文件,就被关了,这样的后果想想就知道了

可以使用flush()函数,强制刷新追加或定入文件,避免文件追加或写入不完整、

格式:变量.flush()

so.flush()

# 路径

文件存放的路径

# 相对路径

相对于你当前程序所在的文件目录

  1. 如果不写 / 等 就代表当前目录
  2. ../ 表示上一层目录

# 绝对路径

  1. 从磁盘根目录开始找起
  2. 互联网的绝对路径

# 只读(r,rb)

操作文件,以只读方式,如果文件不存在 报错

格式:变量.read()

r 操作

r 是读取文件内容的操作

so = open("文件",mode="r",encoding="utf-8")
wo = so.read()
print(wo)
so.close()

执行结果:
手游欢迎你

​ 以上实例,记得使用完文件后要记得关闭文件句柄,文件存储在本目录下,如果存放在别的目录,建议使用相对路径

rb 操作

rb 读取出来的数据是bytes类型,所有在rb模式下,不能有encoding选项

rb 的作用:在读取非文件的时候,比如读取MP3,图像,视频等信息的时候就需要用到

rb 因为这种数据没办法直接显示出来 ,文件上传下载的时候还会用到

直播中实际就是这种数据

so = open("文件",mode="rb")
wo = so.read()
print(wo)
so.close()

执行结果:
b'\xe6\x89\x8b\xe6\xb8\xb8\xe6\xac\xa2\xe8\xbf\x8e\xe4\xbd\xa0'

# 读取文件的方法和方式

# 指定范围读取 - read(n)

read(n) 读取n个字符

如果是 rb 模式,读取出来的是 n 个字节

以 1 为开头

注意:开始读取是,是从头开始读的,如果再次读取,那么会在上一次读取的位置上在去读

格式:变量 = 变量.read(n)

# r 模式单次读取

so = open("文件",mode="r",encoding="utf-8")
wo = so.read(3)
print(wo)
so.close()

执行结果:
手游欢

​ 以上实例,使用指定范围 3 读取,从开头到第3个字符结束

# rb 模式单次读取

so = open("文件",mode="rb")
wo = so.read(3)
print(wo)
so.close()

执行结果:
b'\xe6\x89\x8b'

​ 以上实例,效果跟上面的实例一样,只是得到的值为bytes类型的数据

# r 模式的多次读取

so = open("文件",mode="r",encoding="utf-8")
wo = so.read(2)
wo1 = so.read(2)
print("第一次读取:",wo)
print("第二次读取:",wo1)
so.close()

执行结果:
第一次读取: 手游
第二次读取: 欢迎

​ 以上实例,开始读取是,是从头开始读的,如果再次读取,那么会在上一次读取的位置上在去读

# 一行一行读取 - readline()

一次读取一行数据

注意:在readline() 结尾都会带有一个\n ,每次读取出来的数据尾部都会有一个\n,可以使用strip() 方法去掉 \n或空格

默认是从第一行开始读,如果还要再次读,就会根据上一次读的位置来分配这次的位置

如果使用strip()函数,去除的话,那么类型会变成列表

格式:变量 = 变量.readline()

so = open("文件",mode="r",encoding="utf-8")
wo = so.readline()
wo1 = so.readline()
wo2 = so.readline()
wo3 = so.readline()
wo4 = so.readline()
print("第一次读取:",wo.split())
print("第二次读取:",wo1)
print("第三次读取:",wo2)
print("第四次读取:",wo3)
print("第五次读取:",wo4)
so.close()

执行结果:
第一次读取: ['手']
第二次读取: 游

第三次读取: 欢

第四次读取: 迎

第五次读取: 你

# for循环

so = open("文件",mode="r",encoding="utf-8")
for line in so:
    print(line)
so.close()

执行结果:
手游欢迎你

手游欢迎你

手游欢迎你

手游欢迎你

手游欢迎你

手游欢迎你

手游欢迎你

# 文件内容全部转成列表 - readlines()

将每一行形成一个元素放在一个列表中,将所有的内容都读取出来,请注意内存

格式:变量 = 变量. readlines()

so = open("文件",mode="r",encoding="utf-8")
wo = so.readlines()
print(wo)
so.close()

执行结果:
['手\n', '游\n', '欢\n', '迎\n', '你']

for循环

so = open("文件",mode="r",encoding="utf-8")
wo = so.readlines()
for i in wo:
    print(i.strip())
so.close()

执行结果:
手
游
欢
迎
你

# 循环读取 - for循环

通过for循环每次读取一行内容,不会产生内存溢出的问题

so = open("文件",mode="r",encoding="utf-8")
for i in so:
    print(i.strip())
so.close()

执行结果:
手
游
欢
迎
你

# 只写(w,wb)

如果写入的时候没有对应的文件,就会创建文件

如果文件存在,就会将原件中原来的内容删除,再写入新内容

格式:变量.write(值)

w 模式

so = open("美女",mode="w",encoding="utf-8")
so.write("化妆是人,遇水是鬼")
so.flush()
so.close()
so = open("美女",mode="r",encoding="utf-8")
wo = so.read()
print(wo)
so.close()

执行结果:
化妆是人,遇水是鬼

​ 以上实例,执行完毕后,如果文件不存在,会在当前目录下创建文件并写入

wb 模式

wb模式,是将bytes类型的数据写入文件中,如果是值,需要使用encode()进行解码

so = open("美女",mode="wb")
so.write("化妆是人,遇水是鬼1".encode("utf-8"))
so.flush()
so.close()
so = open("美女",mode="r",encoding="utf-8")
wo = so.read()
print(wo)
so.close()

执行结果:
化妆是人,遇水是鬼1

​ 以上实例,执行完毕后,如果文件不存在,会在当前目录下创建文件并写入

# 追加(a,ab)

追加,写入的内容会追加到文件的结尾处

如果文件不存在,默认会生成文件并追加写入数据

格式:变量.write(值)

a 模式

so = open("美女",mode="a",encoding="utf-8")
so.write("你是人是鬼")
so.flush()
so.close()
so = open("美女",mode="r",encoding="utf-8")
wo = so.read()
print(wo)
so.close()

执行结果:
化妆是人,遇水是鬼1你是人是鬼你是人是鬼

​ 以上实例,执行完毕后,如果文件不存在,会在当前目录下创建文件并追加 写入

ab 模式

ab模式,是将bytes类型的数据追加写入文件中,如果是值,需要使用encode()进行解码

so = open("美女",mode="ab")
so.write("你是人是鬼".encode("utf-8"))
so.flush()
so.close()
so = open("美女",mode="r",encoding="utf-8")
wo = so.read()
print(wo)
so.close()

执行结果:
化妆是人,遇水是鬼1你是人是鬼

​ 以上实例,执行完毕后,如果文件不存在,会在当前目录下创建文件并追加 写入

# 读写(r+)

对于读写模式,必须是先读,因为默认光标是在开头的,写入就需要到结尾处

注意:在r+模式中,必须是要先读取,然后再写入

关于r+ 有个深坑,在r+模式下,如果读取了内容,无论读取内容多少,光标显示的是多少,再写入都是在结尾进行的操作

so = open("美女",mode="r+",encoding="utf-8")
wo = so.read()
so.write("你好,大家好")
print(wo)
so.flush()
so.close()

执行结果:
你好,大家好

# 写读(w+)

对于写读模式,先将文件中所有内容清空,然后在定稿,最后读取,但是读取的内容是空的

可以先写后,光标的位置就在文件内容的结尾,在读就没能读出什么东西来

如果想要读得出东西来,就要使用seek()函数

so = open("美女",mode="w+",encoding="utf-8")
so.write("你好,大家好")
so.seek(0)
wo = so.read()
print(wo)
so.flush()
so.close()

执行结果:
你好,大家好

# 光标位置移动 - seek()

光标?,光标就是在操作文件中当前位置的所在处

seek(),可以指定光标移动到n个位置中

注意:移动的单位是byte字节,如果编码为UTF-8的中文部分,3个节点 = 一个中文

移动到开头:seek(0)

移动到结尾:seek(0,2)

seek()的第一个参数是不可动的,固定为0

seek()的第二个参数表示的是从那个位置进行偏移,如果不填,默认是0

seek()的第二个参数:

  • 0 :为开头
  • 1:为当前位置
  • 2:为结尾

# 光标移动至开头

so = open("文件",mode="r+",encoding="utf-8")
so.seek(0)
wo = so.read()
print(wo)
so.close()

执行结果:
手游欢迎你

# 光标移动至结尾

so = open("文件",mode="r+",encoding="utf-8")
so.seek(0,2)
wo = so.read()
print(wo)
so.close()

执行结果:

# 查看光标的当前位置 - tell()

可以通过tell()来查看光标的当前位置

注意:通过tell() 查出来的是以byte字节为单位,如果编码为UTF-8的中文部分,3个节点 = 一个中文

格式:变量.tell()

so = open("文件",mode="r+",encoding="utf-8")
so.seek(0,1)
wo = so.read(3)
print(so.tell())
so.close()

执行结果:
9

# 截断文件内容 - truncate()

如果要使用截断操作,要先挪动光标,挪动到你想截断的僧,然后再进行截断

默认是截断当前光标位置后的所有内容

关于truncate(n), 如果给出了了n. 则从开头开头进⾏截断, 如果不给n, 则从当前位置截断. 后面的内容将会被删除

格式:变量.truncate()

截断文件中除了前两位的字符,其他都截断

so = open("文件",mode="r+",encoding="utf-8")
so.seek(6)
so.truncate()
so.seek(0)
wo = so.read()
print(wo)
so.flush()
so.close()

执行结果:
手游

# 指定保留字符数截断

注意:截断的单位是byte字节,如果编码为UTF-8的中文部分,3个节点 = 一个中文

保留前3位字符

so = open("文件",mode="r+",encoding="utf-8")
so.truncate(9)
so.seek(0)
wo = so.read()
print(wo)
so.flush()
so.close()

执行结果:
手游欢

# 文件修改,通过 r+ 跟 w 模式 修改文件

so = open("文件",mode="r+",encoding="utf-8")
wo = so.read()
sh = wo.replace("我","你")
ss = open("文件",mode="w",encoding="utf-8")
so.write(sh)
so.flush()
so.close()


加载要修改的文件,打开文件模式为:r+
读取文件内容到内存中
通过字符串函数:replace(), 修改你要修改的内容,实际修改在内存中修改
在次加载要修改的文件,打开文件模式为:w
写入在内存中修改完毕的数据,因为 w 模式的特殊性:写入会清空文件的所有内容,也相当覆盖
刷新文件句柄
关闭文件句柄

​ 以上实例,这种方式没办法使用for循环一行一行的读取修改,请使用者三思

# 文件的修改以及另一种打开文件句柄的方式

文件修改,实际只能将文件中的内容读取到内存中,将信息修改完毕,然后写入另一个文件中,把源文件删除,将新文件的名字改成老文件的名字

文件修改需要应用一个新的打开文件方式,os模块

如果要一次打开多个文件,可以使用 , 逗号来分隔 open

格式:with open("文件名",mode="打开文件的方式权限",encoding="用什么编码格式打开文件") as 变量名 :

格式:os.remove(文件名) ## 删除文件

格式:os.rename("文件名","新文件名") ## 文件名更改

import os
with open("文件",mode="r",encoding="utf-8") as so , open("文件_os",mode="w",encoding="utf-8") as wo:
    sh = so.read()
    ssh = sh.replace("你","我")
    wo.write(ssh)
os.remove("文件")
os.rename("文件_os","文件")



加载OS模块
使用另一种打开文件的方式,打开二个文件,1:为要修改的文件,2:临时修改替换文件,用 , 逗号分隔
	读取要修改的文件
    修改内存中读取过的文件数据,使用字符串函数,replace()替换
    将修改完的数据写入 2 文件中(临时修改替换文件)
使用os自带的函数,删除 1 文件(要修改的文件)
使用os自带的函数,更改 2 文件的文件名 (临时修改替换文件)

​ 以上实例,读取文件的时候是将文件的所有内容一次读取,这样可能会有内存溢出,建议一行一行读取操作

一行一行读取修改文件内容

import os
with open("文件",mode="r",encoding="utf-8") as so , open("文件_os",mode="w",encoding="utf-8") as wo:
    for i in so.read():
        ssh = i.replace("你", "我")
        wo.write(ssh)
os.remove("文件")
os.rename("文件_os", "文件")

​ 以上实例,只是把读取文件操作换成for循环来读取而已